3.3 API Integration
On top of providing user flow for managing the link shortener, Blink also allows APIs to integrate with it via OAuth2. More specifically, this means that you can configure Blink to be called by not just session-based users who are using it via the web portal (the UI), but also to be called by external APIs in a controllable manner so that you may write APIs that "integrate" with Blink without exposing Blink's APIs, unprotected, to the entirety of the internet.
Configuration
First off, breathe.
All of the OAuth2 configuration is done by the OAUTH2_
prefixed environment variables as you may see in the .env
file.
First, note that OAUTH2_ENABLED
is, by default, set to true
. Set it to false
if you don't plan on exposing the API via OAuth2.
The algorithms, audience, and the issuer should be copy-pasted from your authorization server. Note that the algorithms variable is a comma-separated "list" of all the algorithms the authorization server allows that you want to use (e.g. RS256,RS512
).
The OAUTH2_JWT_SECRET
and OAUTH2_JWKS_*
variables are mutually exclusive. The former allows you to simply decode the JWTs that the APIs present using a string or a key that the authorization server and Blink share; for symmetric encryption algorithms such as HS256
, this would be the actual private key. For asymmetric ones, it would be the public key, as verification of the JWT is possible without having the key that it was encrypted with.
The latter allows you to dynamically load in the shared secret to verify the JWTs; this would be useful if, for example, your authorization server had a rotating set of keys to encrypt and verify the JWTs against, so that Blink can "pick up" on those rotating set of keys without having to hard code them. See https://github.com/auth0/express-jwt#multi-tenancy for more details.
And finally, the OAUTH2_DEFAULT_SCOPE
describes the default scope the JWTs will be assigned if the JWT is missing the scope
claim. It is a space-separated "list" of the scopes, and the * serves as a wildcard; so, for example, link:*
would include link:create
, link:read
, link:update
, and link:delete
.
Of note, these scopes restrict what an authorized API could do on top of the built-in policies that dictate what a superuser
can and cannot do (it's not an either/or). So, for example, even with a link:update
scope, you cannot update the shortened link after it's already been set.
Recommendations
First off, I understand that OIDC is a protocol built on top of OAuth2; however, I suggest that you do not re-use the OIDC provider that you use as the "user pool" as the authorization server as well. Just, don't.
In addition, always always always be sure to fill out the audience
and issuer
for additional security. Not all authorization servers might outright tell you what these values are; a good way to figure this out is to paste a sample token from the authorization server onto https://jwt.io or something similar and seeing the scopes for those two in the decoded payload.
As for secrets, if you're using OAUTH2_JWT_SECRET
, please do use a long string (at least 32 characters); if not (and this depends on what your authorization server supports), it's best to rely on JWKS to rotate actual keys. Not only is it more robust, but it also allows the authorization server to "rotate" keys that it signs the JWTs with so that should any specific key be compromised, it can quickly "advertise" to its dependents (including Blink) to not use that key to validate.
And finally, try if possible to set the scope in JWTs; obviously, this depends on whether your authorization server supports it, but please do it if you can. In addition, always set the scope to the bare minimum to support a consuming API's operations. If it doesn't need to read the users, then don't include the user:read
scope, and such.